﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;

namespace Alex_s_Dream_The_Toy_World
{
    public class FishAIComponent : AIComponent
    {
        private Node startNodeOfPath;

        private System.Drawing.Point begin = new System.Drawing.Point(10, 10);
        private System.Drawing.Point end = new System.Drawing.Point(60, 60);

        private float forwardMovement = 0.0f;      // Movement forward/backward this frame
        private float rightMovement = 0.0f;        // Movement right/left this frame
        private float clockwiseRotation = 0.0f;    // Rotation around Y axis this frame
        private bool wantsJump = false;

        private float movementSpeed = 400.0f;
        //must less than or equal Pi/2
        private float rotationSpeed = (float)Math.PI / 180.0f;      //2 Degree per Rotation

        public FishAIComponent(BaseEntity parent)
            : base(parent)
        {
            // Get Terrain Entity
            MsgGetTerrainEntity getTerrainMsg = ObjectPool.Aquire<MsgGetTerrainEntity>();
            getTerrainMsg.UniqueTarget = this.parentEntity.Game.TerrainID;  
            this.parentEntity.Game.SendMessage(getTerrainMsg);

            // Get Map
            CurrentMap = getTerrainMsg.TerrainEntity.Map;
            startNodeOfPath = FindPath(begin, end);
        }

        public override bool IsWalkable(System.Drawing.Point point)
        {
            bool walkable = false;
            switch (CurrentMap.PointList[point.X, point.Y])
            {
                case TerrainPoint.GROUND:
                    walkable = true;
                    break;
                case TerrainPoint.WATER:
                    walkable = true;
                    break;
                default:
                    walkable = false;
                    break;
            }
            return walkable;
        }

        /// <summary>
        /// This is the component's Update loop, it takes care of placing and moving the camera each frame.
        /// </summary>
        /// <param name="gameTime">XNA's built-in timer, provides a snapshot of the game's time.</param>
        public override void Update(GameTime gameTime)
        {
            // Have movement path
            if (startNodeOfPath != null)
            {
                // Get Position of AI
                MsgGetPosition getPosMsg = ObjectPool.Aquire<MsgGetPosition>();
                getPosMsg.UniqueTarget = this.parentEntity.UniqueID;
                this.parentEntity.Game.SendMessage(getPosMsg);

                if (getPosMsg.Position.Y < 0)
                    throw new Exception("Height");
                //Translate to XZPosition
                System.Drawing.Point position = CurrentMap.ConvertNomalPositionToMapPosition(getPosMsg.Position);
                if (position == startNodeOfPath.Position)
                {
                    SetNextPositionToMove();
                }
                else
                {
                    if (!Rotate(position, startNodeOfPath.Position))
                        Move();
                }
            }
            else
            {
                Stop();
                StopRotate();
            }
        }
        private void StopRotate()
        {
            clockwiseRotation = 0;
        }

        private void Stop()
        {
            forwardMovement = 0;
        }

        private void Move()
        {
            forwardMovement = movementSpeed;
            StopRotate();
        }

        private void SetNextPositionToMove()
        {
            if (startNodeOfPath.NextNode != null)
            {
                startNodeOfPath = startNodeOfPath.NextNode;
            }
            else
            {
                startNodeOfPath = null;
                Stop();
                StopRotate();
            }
        }

        private bool Rotate(System.Drawing.Point begin, System.Drawing.Point end)
        {
            Stop();

            Vector3 movingDirect = CurrentMap.ConvertMapPositionToNomalPosition(begin) - CurrentMap.ConvertMapPositionToNomalPosition(end);
            Vector3 forwardDirect = this.parentEntity.Rotation.Forward;
            Vector3 rightDirect = this.parentEntity.Rotation.Right;

            float angle = MathGuide.GetSignedAngleBetweenTwoVectors(movingDirect, forwardDirect, rightDirect);

            if (angle == 0 || Math.Abs(angle) < 0.004 || Math.Abs(angle) > 3.137)
            {
                return false;
            }
            else
            {
                //if (Math.Abs(angle) >= rotationSpeed)
                //    clockwiseRotation = rotationSpeed * (-angle / Math.Abs(angle));
                //else
                //    clockwiseRotation = -angle;
                //return true;
                if (0 < angle && angle <= Math.PI / 2)
                {
                    if (angle >= rotationSpeed)
                        clockwiseRotation = rotationSpeed * (-angle / Math.Abs(angle));
                    else
                        clockwiseRotation = -angle;
                    return true;
                }
                if (Math.PI / 2 < angle && angle < Math.PI)
                {
                    if (angle >= rotationSpeed)
                        clockwiseRotation = rotationSpeed * (angle / Math.Abs(angle));
                    else
                        clockwiseRotation = angle;
                    return true;
                }
                if (-Math.PI / 2 <= angle && angle < 0)
                {
                    if (Math.Abs(angle) >= rotationSpeed)
                        clockwiseRotation = rotationSpeed * (-angle / Math.Abs(angle));
                    else
                        clockwiseRotation = -angle;
                    return true;
                }
                if (-Math.PI < angle && angle < -Math.PI / 2)
                {
                    if (Math.Abs(angle) >= rotationSpeed)
                        clockwiseRotation = rotationSpeed * (angle / Math.Abs(angle));
                    else
                        clockwiseRotation = angle;
                    return true;
                }
                return false;
            }
        }

        /// <summary>
        /// Handles a message sent to this component.
        /// </summary>
        /// <param name="message">Message to be handled</param>
        /// <returns>True, if handled, otherwise false</returns>
        public override bool ExecuteMessage(IMessage message)
        {
            switch (message.Type)
            {
                case MessageType.GetAIMovementInfo:
                    {
                        MsgGetAIMovement msgGetMovement = message as MsgGetAIMovement;
                        message.TypeCheck(msgGetMovement);

                        msgGetMovement.ForwardAmount = this.forwardMovement;
                        msgGetMovement.RightAmount = this.rightMovement;
                        msgGetMovement.ClockwiseAmount = this.clockwiseRotation;
                        msgGetMovement.WantsJump = this.wantsJump;
                    }
                    return true;
                default:
                    return false;
            }
        }
    }
}
